package yl.hs.bmipfull.service.finance.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import yl.hs.bmipfull.daos.finance.IBrickShipSettleDetailDao;
import yl.hs.bmipfull.daos.finance.IBrickShipSettleMainDao;
import yl.hs.bmipfull.daos.finance.ISettleAdditionDao;
import yl.hs.bmipfull.daos.finance.ISettleOrderDao;
import yl.hs.bmipfull.daos.laboratory.IProductCateDao;
import yl.hs.bmipfull.daos.laboratory.IProductInfoDao;
import yl.hs.bmipfull.daos.salemanage.ISaleContractDao;
import yl.hs.bmipfull.pojo.finance.BrickShipSettleDetailEntity;
import yl.hs.bmipfull.pojo.finance.BrickShipSettleMainEntity;
import yl.hs.bmipfull.pojo.finance.SettleAdditionEntity;
import yl.hs.bmipfull.pojo.finance.SettleOrderEntity;
import yl.hs.bmipfull.pojo.production.BrickShipOrderModel;
import yl.hs.bmipfull.service.finance.IBrickShipSettleService;
import yl.hs.bmipfull.utils.HsApp;
import yl.hs.bmipfull.utils.SearchFilterEngine;
import yl.hs.bmipfull.utils.Tl;
import yl.hs.bmipfull.viewmodels.PageQuery;
import yl.hs.bmipfull.viewmodels.PageResult;
import yl.hs.bmipfull.viewmodels.SubmitModel;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/*
 * BrickShipSettleServiceImpl
 * @category
 * @author 谢景广
 * @date 2023-01-03
 */
@Service
public class BrickShipSettleServiceImpl extends FinanceService implements IBrickShipSettleService {
    @Autowired
    IProductCateDao cateDao;
    @Autowired
    IProductInfoDao infoDao;
    @Override
    public PageResult<Map<String, Object>> queryPage(PageQuery query, int rowNum, int page) throws Exception {
        PageResult<Map<String,Object>>  result;
        query.checkSortSides(BrickShipSettleMainEntity.class);
        var user = HsApp.getCurrent();
        if(user!=null){
            query.getQuery().put("types", user.getType()==null?0:user.getType());
        }
        var filters = SearchFilterEngine.generateQueryList(query.getFilters(), BrickShipSettleMainEntity.class);
        List<Map<String,Object>> data = new ArrayList<>();
        if(rowNum>0){
            result = new PageResult<>(rowNum);
            query.getQuery().put("querySkip", (page - 1) * rowNum);
            query.getQuery().put("pageSize", rowNum);
            result.setRecords(mainDao.queryCountByCondition(query.getQuery(),filters));
            result.setPage(page);
            var list = mainDao.queryPage(query.getQuery(),filters);
            for (var item : list) {
                data.add(Tl.toMap(item));
            }
        }
        else
        {
            var searchQuery = query.getQuery();
            if(user!=null){
                query.getQuery().put("types", user.getType()==null?0:user.getType());
            }
            if(searchQuery.get("productCateSN")==null)
            {
                throw new Exception("搜索条件中缺少productCateSN");
            }
            var defFilters = query.getFilters();
            if(defFilters!=null && defFilters.size()>0){
                var startTimeOpt = defFilters.stream().filter(t->t.getField().equals("startTime")).findFirst();
                var endTimeOpt = defFilters.stream().filter(t->t.getField().equals("endTime")).findFirst();
                startTimeOpt.ifPresent(queryFilter -> searchQuery.put("startTime", queryFilter.getSearchVal()));
                endTimeOpt.ifPresent(queryFilter -> searchQuery.put("endTime", queryFilter.getSearchVal()));
            }
            var orderList = mainDao.queryTempSettleOrders(searchQuery,filters);
            var contractList = mainDao.querySettleContract(searchQuery,filters);
            var contractLastSettle = mainDao.queryContractSettleLast(searchQuery,filters);
            var settleReceiptList = mainDao.queryContractReceiptList(searchQuery,filters);//收款信息
            result = new PageResult<>(contractList.size()>0?contractList.size():1);
            result.setRecords(contractList.size());
            result.setPage(page);
            for (var cti : contractList) {
                BrickShipSettleMainEntity item = new BrickShipSettleMainEntity();
                item.setSn(cti.getSn());
                var orders = orderList.stream().filter(t -> cti.getSn().equals(t.getContractSN()) && t.getState()==10).collect(Collectors.toList());
                item.setContractRemark(cti.getRemark());
                item.setContractSN(cti.getSn());
                item.setContractingUnit(cti.getContractingUnit());
                item.setCustomerName(cti.getCustomerName());
                item.setCustomerSN(cti.getCustomerSN());
                item.setProductCateSN(searchQuery.get("productCateSN").toString());
                item.setSettleTime(new Date());
                Date min, max;
                var searchStart = searchQuery.get("startTime");
                var searchEnd = searchQuery.get("endTime");
                var dataParse = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                if (searchStart == null || "".equals(searchStart)) {
                    var minOpt = orders.stream().map(BrickShipOrderModel::getOutTime).filter(Objects::nonNull).min(Date::compareTo);
                    if(minOpt.isPresent()){
                        min = minOpt.get();
                    }
                    else {
                        Calendar st = Calendar.getInstance();
                        st.setTime(new Date());
                        st.add(Calendar.YEAR,-1);
                        min = st.getTime();
                    }
                } else {
                    min = dataParse.parse(searchStart.toString());
                }
                if (searchEnd == null || "".equals(searchEnd)) {
                    var maxOpt = orders.stream().map(BrickShipOrderModel::getOutTime).filter(Objects::nonNull).max(Date::compareTo);
                    max = maxOpt.orElseGet(Date::new);
                } else {
                    max = dataParse.parse(searchEnd.toString());
                }
                item.setStartTime(min);
                item.setEndTime(max);
                var lastSettle = contractLastSettle.stream().filter(t -> t.getContractSN().equals(cti.getSn())).findFirst();
                BigDecimal previousAmount = BigDecimal.valueOf(0.0),
                        totalReceivedAmount = BigDecimal.valueOf(0.0), currentReceivedAmount = BigDecimal.valueOf(0.0),
                        totalQuantity = BigDecimal.valueOf(0.0), currentQuantity = BigDecimal.valueOf(0.0),
                        totalAmount = BigDecimal.valueOf(0.0), currentAmount = BigDecimal.valueOf(0.0),
                        unAQuantity = BigDecimal.valueOf(0.0), unAAmount = BigDecimal.valueOf(0.0),//已审核未结算
                        unQuantity = BigDecimal.valueOf(0.0), unAmount = BigDecimal.valueOf(0.0);//未审核未结算
                int totalOrderCount = 0, unAOrderCount = 0, unOrderCount = 0;
                item.setPriceChannel(cti.getChannel());
                item.setSettleWay("周期");
                if (lastSettle.isPresent()) {
                    var last = lastSettle.get();
                    previousAmount = last.getCurrentAmount();
                    totalReceivedAmount = last.getTotalReceivedAmount();
                    totalQuantity = last.getTotalVolume();
                    totalAmount = last.getTotalAmount();
                    totalOrderCount = last.getTotalOrderCount();
                } else {
                    if (cti.getInitialBatch() != null) {
                        totalQuantity = totalQuantity.add(cti.getInitialBatch());
                    }
                    if (cti.getInitialMoney() != null) {
                        totalAmount = totalAmount.add(cti.getInitialMoney());
                    }
                }
                for (var order : orders) {
                    BigDecimal price = order.getPrice();
                    if (order.getState() == 10) {//已审核
                        currentQuantity = currentQuantity.add(order.getShipmentReviewQuantity());
                        currentAmount = currentAmount.add(order.getShipmentReviewQuantity().multiply(price));
                    }
                    else {
                        unAmount =unAmount.add(order.getVolume().multiply(order.getPrice()));
                        unQuantity = unQuantity.add(order.getVolume());
                    }
                }
                var contractReceiptAmountObj = settleReceiptList.stream().filter(t -> cti.getSn().equals(t.getSn())).findFirst();
                if (contractReceiptAmountObj.isPresent()) {
                    currentReceivedAmount = contractReceiptAmountObj.get().getAmount();
                    totalReceivedAmount = totalReceivedAmount.add(currentReceivedAmount);
                }
                totalOrderCount += orders.stream().map(BrickShipOrderModel::getOrderSN).distinct().toList().size();
                item.setCurrentVolume(currentQuantity);
                item.setTotalVolume(totalQuantity.add(currentQuantity));
                item.setPreviousAmount(previousAmount);
                item.setCurrentAmount(currentAmount);
                item.setTotalAmount(totalAmount.add(currentAmount));
                item.setTotalReceivedAmount(totalReceivedAmount);
                item.setCurrentReceivedAmount(currentReceivedAmount);
                item.setCurrentOrderCount(orders.stream().map(BrickShipOrderModel::getOrderSN).distinct().toList().size());
                item.setTotalOrderCount(totalOrderCount);
                item.setRemark("");
                item.setProductCateName("");
                item.setState((short) 1);
                item.setTags("");
                var dataMap = Tl.toMap(item);
                dataMap.put("unSettleApprovedQuantity", unAQuantity);
                dataMap.put("unSettleApprovedAmount", unAAmount);
                dataMap.put("unSettleApprovedOrderCount", unAOrderCount);
                dataMap.put("unApprovedQuantity", unQuantity);
                dataMap.put("unApprovedAmount", unAmount);
                dataMap.put("unApprovedOrderCount", unOrderCount);
                data.add(dataMap);
            }
        }
        result.setData(data);
        return result;
    }

    @Override
    public Map<String, Object> querySettleDetailsBySettleSN(String settleSN) {
        Map<String,Object> result = new HashMap<>();
        var details = detailDao.queryBySettleSN(settleSN);
        details.forEach(s->{
            var info = infoDao.findBySN(s.getProductSN());
            if(info!=null){
                var cate = cateDao.findBySN(info.getProductCateSN());
                if(cate!=null){
                    s.setProductCate(cate.getCateName());
                }
            }
        });
        result.put("details", details);
        result.put("additions",additionDao.queryAdditionsBySettleSN(settleSN));
        return result;
    }

    @Override
    public List<Map<String, Object>> queryTempSettleDetails(String contractSN, String startDate, String endDate) throws ParseException {
        var start = parseTime(startDate,true);
        var end = parseTime(endDate,false);
        List<Map<String,Object>> list = new ArrayList<>();
        var data = detailDao.queryTempDetails(contractSN,start,end);
        for (var order : data){
            if(order.getAccountProductSN() == null){
                order.setAccountProductSN(order.getProductSN());
            }
            if(order.getAccountProductName() == null){
                order.setAccountProductName(order.getProductName());
            }
            if(order.getAccountProductSpec() == null){
                order.setAccountProductSpec(order.getProductSpec());
            }
        }
        var psnGroupData = data.stream().collect(
                Collectors.groupingBy(BrickShipOrderModel::getAccountProductSN,
                Collectors.groupingBy(BrickShipOrderModel::getAccountProductName,
                        Collectors.groupingBy(BrickShipOrderModel::getAccountProductSpec,
                                Collectors.groupingBy(BrickShipOrderModel::getPrice,Collectors.toList())))));
        for (var productSN: psnGroupData.keySet()){
            var pNameGroup = psnGroupData.get(productSN);
            for (var productName : pNameGroup.keySet()){
                var pSpecGroup = pNameGroup.get(productName);
                for (var productSpec : pSpecGroup.keySet()){
                    var priceGroup = pSpecGroup.get(productSpec);
                    for (var price : priceGroup.keySet()) {
                        var orders = priceGroup.get(price);
                        BrickShipSettleDetailEntity detail = new BrickShipSettleDetailEntity();
                        var minDate = orders.stream().min(Comparator.comparing(BrickShipOrderModel::getOutTime));
                        var maxDate = orders.stream().max(Comparator.comparing(BrickShipOrderModel::getOutTime));
                        String minTime = "", maxTime = "";
                        var dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        if (minDate.isPresent()) {
                            minTime = dateFormat.format(minDate.get().getOutTime());
                        }
                        if (maxDate.isPresent()) {
                            maxTime = dateFormat.format(maxDate.get().getOutTime());
                        }
                        detail.setTitle(String.format("%s 至 %s", minTime, maxTime));
                        detail.setOrderCount(orders.stream().map(BrickShipOrderModel::getOrderSN).distinct().toList().size());
                        detail.setProductSN(productSN);
                        detail.setProductName(productName);
                        detail.setProductCate(orders.get(0).getProductCate());
                        detail.setProductSpec(productSpec);
                        detail.setPrice(price);
                        var totalQuantity = orders.stream().map(BrickShipOrderModel::getShipmentReviewQuantity).reduce(BigDecimal.ZERO,BigDecimal::add);
                        detail.setTotalQuantity(totalQuantity);
                        var totalVolume = orders.stream().map(BrickShipOrderModel::getShipmentReviewVolume).reduce(BigDecimal.ZERO,BigDecimal::add);

                        detail.setTotalVolume(totalVolume);
                        detail.setAmount(totalQuantity.multiply(price));
                        detail.setFreight(BigDecimal.ZERO);
                        var map = detail.toMap();
                        map.put("orderSnArray",orders.stream().map(BrickShipOrderModel::getSn).toList());
                        list.add(map);
                    }
                }
            }
        }
        return list;
    }

    @Override
    public List<BrickShipOrderModel> queryOrdersSettleDetails(String contractSN, String startDate, String endDate) throws ParseException {
        var start = parseTime(startDate,true);
        var end = parseTime(endDate,false);
        return detailDao.queryTempDetails(contractSN,start,end);
    }

    @Override
    public Integer cancelSettle(String settleSN) {
        var result = mainDao.deleteBySettleSN(settleSN);
        detailDao.deleteBySettleSN(settleSN);
        additionDao.deleteBySettleSN(settleSN);
        var snList = orderDao.queryOrderSnBySettleSN(settleSN);
        if(snList.size()>0){
            mainDao.changeOrderDetailToUnSettleByOrderSN(snList);
            mainDao.changeOrderMainToUnSettleByOrderSN(snList);
        }
        orderDao.deleteBySettleSN(settleSN);
        return result;
    }

    @Override
    public Integer saveSettle(SubmitModel model) throws Exception {
        int result;
        var settleMainInfo = new BrickShipSettleMainEntity();
        Tl.getChangeLog(settleMainInfo,model.getData());
        var settleSN = queryNewSN(settleMainInfo.getClass());
        settleMainInfo.setSn(settleSN);
        settleMainInfo.setSettleTime(new Date());
        settleMainInfo.setState((short) 1);
        settleMainInfo.setPYFieldValue();
        settleMainInfo.setOrganizationCode(getDepartSN());
        if(!Tl.isEmpty(settleMainInfo.getContractSN())){
            var contract=contractDao.findBySN(settleMainInfo.getContractSN());
            settleMainInfo.setContractingUnit(contract.getContractingUnit());
        }
        var lastSettle = mainDao.getLastSettle(settleMainInfo.getContractSN());
        if(lastSettle!=null) {
            settleMainInfo.setPreviousAmount(lastSettle.getCurrentAmount());
            settleMainInfo.setTotalAmount(lastSettle.getTotalAmount().add(settleMainInfo.getCurrentAmount()));
            settleMainInfo.setTotalOrderCount(lastSettle.getTotalOrderCount() + settleMainInfo.getCurrentOrderCount());
            settleMainInfo.setTotalVolume(lastSettle.getTotalVolume().add(settleMainInfo.getCurrentVolume()));
            settleMainInfo.setTotalReceivedAmount(lastSettle.getTotalReceivedAmount().add(settleMainInfo.getCurrentReceivedAmount()));
        }
        else {
            settleMainInfo.setTotalAmount(settleMainInfo.getCurrentAmount());
            settleMainInfo.setTotalReceivedAmount(settleMainInfo.getCurrentReceivedAmount());
            settleMainInfo.setTotalVolume(settleMainInfo.getCurrentVolume());
            settleMainInfo.setTotalOrderCount(settleMainInfo.getCurrentOrderCount());
        }
        var settleOrderSNList = new ArrayList<String>();
        result = mainDao.insertNew(settleMainInfo);
        if(result>0) {
            var detailsObj = model.getData().get("details");
            if(detailsObj!=null){
                if(detailsObj instanceof List<?> detailList ) {
                    if (detailList.size() > 0) {
                        for (Object detailMap : detailList) {
                            if (detailMap instanceof Map<?, ?> map) {
                                var orderSnObj = map.get("orderSnArray");
                                var detailItem = new BrickShipSettleDetailEntity();
                                var detailSN = queryNewSN(BrickShipSettleDetailEntity.class);
                                Tl.getChangeLog(detailItem, map);
                                detailItem.setSn(detailSN);
                                detailItem.setSettleSN(settleSN);
                                detailDao.insertNew(detailItem);
                                if (orderSnObj != null) {
                                    if (orderSnObj instanceof List<?> orderColl) {
                                        if (orderColl.size() > 0) {
                                            for (var snObj :  orderColl) {
                                                var settleOrder = new SettleOrderEntity();
                                                settleOrder.setSettleSN(settleSN);
                                                settleOrder.setDetailSN(detailSN);
                                                var orderSN = snObj.toString();
                                                settleOrder.setOrderSN(orderSN);
                                                settleOrderSNList.add(orderSN);
                                                orderDao.insertNew(settleOrder);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            var additionsObj = model.getData().get("additions");
            if (additionsObj!=null){
                if(additionsObj instanceof List<?> additionArray){
                    if(additionArray.size()>0){
                        for (Object mapObj : additionArray) {
                            if (mapObj instanceof Map<?, ?> map) {
                                var additionItem = new SettleAdditionEntity();
                                additionItem.setSn(queryNewSN(additionItem.getClass()));
                                Tl.getChangeLog(additionItem, map);
                                additionItem.setSettleSN(settleSN);
                                additionDao.insertNew(additionItem);
                            }
                        }
                    }
                }
            }
            //仅发送财务人员
            sendConstructMessage("有新的结算单"+settleMainInfo.getSn()+"需要确认",settleMainInfo.getContractSN(),"结算单确认",(short) 2);
        }
        if(settleOrderSNList.size()>0) {
            mainDao.changeOrderDetailToSettled(settleOrderSNList);
            mainDao.changeOrderMainToSettled(settleOrderSNList);
        }
        return  result;
    }

    @Override
    public List<BrickShipOrderModel> querySettleOrdersBySettleSN(String settleSN) {
        return mainDao.querySettleOrdersBySettleSN(settleSN);
    }

    @Override
    public List<BrickShipOrderModel> querySettleOrdersBySnList(String snList) {
        return mainDao.querySettleOrdersBySnArray(snList.split(","));
    }

    private ISettleOrderDao orderDao;

    @Autowired
    public void setOrderDao(ISettleOrderDao orderDao) {
        this.orderDao = orderDao;
    }

    private ISettleAdditionDao additionDao;

    @Autowired
    public void setAdditionDao(ISettleAdditionDao additionDao) {
        this.additionDao = additionDao;
    }

    private IBrickShipSettleMainDao mainDao;

    @Autowired
    public void setMainDao(IBrickShipSettleMainDao mainDao) {
        this.mainDao = mainDao;
    }

    private IBrickShipSettleDetailDao detailDao;

    @Autowired
    public void setDetailDao(IBrickShipSettleDetailDao detailDao) {
        this.detailDao = detailDao;
    }

    private ISaleContractDao contractDao;

    @Autowired
    public void setContractDao(ISaleContractDao contractDao) {
        this.contractDao = contractDao;
    }

    @Override
    public List<BrickShipOrderModel> brickSaleStatistics(PageQuery pageQuery) {
        return mainDao.brickSaleStatistics(pageQuery.getQuery());
    }

    @Override
    public int brickCountCars(PageQuery pageQuery) {
        return mainDao.brickCountCars(pageQuery.getQuery());
    }
    protected Date parseTime(String date,boolean start) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if(!Tl.isEmpty(date)){
            if(start) {
                return sdf.parse(date);
            }
            var time = sdf.parse(date);
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(time);
            calendar.add(Calendar.MINUTE,2);
            return calendar.getTime();
        }
        else if(start) {
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(new Date());
            calendar.add(Calendar.YEAR, -1);
            return  calendar.getTime();
        }else{
            return new Date();
        }
    }
}
